package dev.loong.boot3.config;

import dev.loong.boot3.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.security.authorization.AuthorizationDecision;
import org.springframework.security.authorization.AuthorizationManager;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.crypto.password.Pbkdf2PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.provisioning.JdbcUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.access.intercept.RequestAuthorizationContext;
import org.springframework.util.StringUtils;

import java.util.ArrayList;
import java.util.List;

@Configuration
public class WebSecurityCustomConfig {
    /**
     * 创建密码编码器
     * @return
     */
//    @Bean
//    public PasswordEncoder initPasswordEncoder() {
//        return new BCryptPasswordEncoder();
//    }
    @Bean
    public PasswordEncoder initPasswordEncoder(@Value("${user.password.encder.secret}") String secret) {
        return new Pbkdf2PasswordEncoder(secret,
                16,
                310000,
                Pbkdf2PasswordEncoder.SecretKeyFactoryAlgorithm.PBKDF2WithHmacSHA512);
    }

    public static void main(String[] args) {
        System.out.println(new Pbkdf2PasswordEncoder("abcdefg",
                16,
                310000,
                Pbkdf2PasswordEncoder.SecretKeyFactoryAlgorithm.PBKDF2WithHmacSHA512).encode("123456"));
    }
    /**
     * 内存用户详情服务
     * @param pwdEncoder
     * @return
     */
//    @Bean
//    public UserDetailsService initInMemoryUserDetailsService(
//            @Autowired PasswordEncoder pwdEncoder
//    ) {
//        GrantedAuthority userAuth = ()->"ROLE_USER";
//        GrantedAuthority adminAuth = ()->"ROLE_ADMIN";
//
//        List<UserDetails> userList = List.of(
//                new User("user1", pwdEncoder.encode("123456"), List.of(userAuth)),
//                new User("admin", pwdEncoder.encode("abcdefg"), List.of(userAuth,adminAuth))
//        );
//        var result = new InMemoryUserDetailsManager(userList);
//        return result;
//    }

    /**
     * 从数据库中获取用户信息
     * @param jdbcTemplate 通过JdbcTemplate获取用户信息
     * @return
     */
//    @Bean
//    public UserDetailsService initUserDetailsService(@Autowired JdbcTemplate jdbcTemplate) {
//        var userSql = """
//                select user_name, pwd, available from t_user where user_name = ?
//                """;
//        var authSql = """
//                select u.user_name, r.role_name
//                from t_user u, t_user_role ur, t_role r
//                where u.id = ur.user_id and r.id = ur.role_id and u.user_name = ?
//                """;
//        var userDetailsService = new JdbcUserDetailsManager();
//        userDetailsService.setUsersByUsernameQuery(userSql);
//        userDetailsService.setAuthoritiesByUsernameQuery(authSql);
//        userDetailsService.setJdbcTemplate(jdbcTemplate);
//        return userDetailsService;
//    }
    @Bean
    public UserDetailsService initUserDetailsService(@Autowired UserService userService) {
        return username -> {
            var userPo = userService.getUserByName(username);
            System.out.println(userPo);
            var authList = new ArrayList<GrantedAuthority>();
            for (var role : userPo.getRoles()) {
                GrantedAuthority auth = () -> role.getRoleName();
                authList.add(auth);
            }
            var userDetails = new User(userPo.getUserName(), userPo.getPwd(), authList);
            return userDetails;
        };
    }

    @Bean
    public SecurityFilterChain securityFilterChain(@Autowired HttpSecurity http) throws Exception {
        http.authorizeHttpRequests(
                auth -> auth.requestMatchers("/", "/users/**", "/csrf/**", "/rabbitmq/**")
                        .access(authMgr("ROLE_USER", "ROLE_ADMIN")) // 使用自定义权限管理器验证权限
                        //.hasAnyRole("USER", "ADMIN")
                        .requestMatchers("/admin/**")
                        .access(authMgr("ROLE_ADMIN"))
                        .requestMatchers("/login/**", "/logout/**")
                        //.hasAuthority("ROLE_ADMIN")
                        //.anyRequest()
                        .permitAll()
        );

        http.rememberMe(
                config -> config.tokenValiditySeconds(86400).rememberMeCookieName("remember-me-key") //.key("remember-me-key")
        );
        http.formLogin(
                form -> form.loginPage("/login/page").defaultSuccessUrl("/login/welcome")
        );
        http.logout(
                logout -> logout.logoutUrl("/logout/page").logoutSuccessUrl("/logout/result").permitAll()
        );
        http.httpBasic(
                config->config.realmName("my-basic-name")
        );
        return http.build();
    }

    /**
     * 设置不拦截请求的资源
     * @return
     */
    @Bean
    public WebSecurityCustomizer initWebSecurity() {
        return web -> {
            web.ignoring().requestMatchers("/css/**", "/js/**", "/images/**", "/fonts/**", "/community/**");
        };
    }
    /**
     * 自定义权限管理验证逻辑，当用户拥有参数中的角色中的一个，就放行请求，否则就拦截请求
     * @param roleNames
     * @return
     */
    private AuthorizationManager<RequestAuthorizationContext> authMgr(String... roleNames) {
        // 参数为空
        if(roleNames == null || roleNames.length == 0) {
            throw new RuntimeException("角色列表不能为空");
        }
        // 转换为列表
        var roleNameList = List.of(roleNames);
        return (authSupplier, reqAuthContext) -> {
            // 获取HttpServletRequest对象
            var request = reqAuthContext.getRequest();
            // 获取请求参数
            var vars = reqAuthContext.getVariables();
            // 当前用户的权限信息
            var auths = authSupplier.get().getAuthorities();
            for (var auth : auths) {
                var roleName = auth.getAuthority();
                // 当前用户拥有对应的角色，放行请求
                if (roleNameList.contains(roleName)) {
                    return new AuthorizationDecision(true);
                }
            }
            return new AuthorizationDecision(false);
        };
    }
}
